﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using Ataric.Themester.Model;
using Ataric.Themester.Parser;

namespace Ataric.Themester.UI
{
    public partial class _Default : System.Web.UI.Page
    {
        #region Variables & Constants
        private const string CSS_CACHE = "CSS CACHE";
        #endregion

        #region Event handlers

        protected override void OnInit(EventArgs e)
        {
            bntUpload.Click += new EventHandler(bntUpload_Click);
            bntSave.Click += new EventHandler(bntSave_Click);
            base.OnInit(e);
        }

        void bntSave_Click(object sender, EventArgs e)
        {
            string content = GetUpdate(_mappings.Value, MergeModes.All);
            CSSParser parser = new CSSParser();
            CSSDocument document = parser.ParseText(content);

            //CSSDocument sharepoint = SessionManager.StaticSharepointCSSDocument;

            //foreach (var rule in document.RuleSets)
            //{
            //    RuleSet sharepointRule = FindRuleSet(rule.Selectors[0].ToString(), sharepoint);
            //        MergeRuleSet(rule, sharepointRule);
            //}

            Response.AddHeader("content-disposition", "attachment; filename=sharepoint.css");

            Response.ContentType = "text/css";
            Response.Write(document.ToString());
            Response.End();
        }

        void bntUpload_Click(object sender, EventArgs e)
        {
            if (uploadFile.HasFile&& Regex.IsMatch(uploadFile.FileName,@".*(?i)\.css$"))
            {
                ProcessUploadFile();
                RebuidMapping();
                
            }
        }

        private void ProcessUploadFile()
        {
            StreamReader reader = new StreamReader(uploadFile.PostedFile.InputStream);

            string css = reader.ReadToEnd();
            reader.Close();

            CSSDocument document = ParseCSS(css);
            //SessionManager.WorkingDocument = document;
            List<CSSDocument> documents = SessionManager.UploadedCSSDocuments;
            documents.Add(document);
            hiddenCurrentIndexSession.Value = (documents.Count - 1).ToString();
            DisplayCSS(document, listSourceCSS);
            SessionManager.UploadedCSSDocuments = documents;
        }

        private void RebuidMapping()
        {
            string mappings = _mappings.Value.TrimEnd(new char[] { '|' });

            string[] arr = mappings.Split(new char[] { '|' });
            listMappingCSS.Items.Clear();
            foreach (string s in arr)
            {
                if (string.IsNullOrEmpty(s)) continue;

                string[] arr1 = s.Split(new char[] { ',' });
                string src = arr1[0];
                string sp = arr1[1];
                string idx = arr1[2];
                Item item = Database.GetItem(Convert.ToInt32(sp));

                listMappingCSS.Items.Add(new ListItem()
                {
                    Text = string.Format("{0} - {1}", src, item.Name),
                    Value = string.Format("{0},{1},{2},", src, item.Id, idx),
                });
            }
        }
        protected void Page_Load(object sender, EventArgs e)
        {

            if (!IsPostBack)
            {
                string css = "";
                PopulateAreaDropDownList();

                InitializeSharepointCSSFromDB();
                DisplayCSS(SessionManager.WorkingDocument,listSourceCSS);

            }
        }

        #endregion

        #region Uility Methods

        private string FormatErrorMessage(List<string> err)
        {
            if (err.Count == 0) return string.Empty;
            StringBuilder sb = new StringBuilder();
            sb.Append("<ul>");
            foreach (string s in err)
                if (!string.IsNullOrEmpty(s))
                {
                    sb.AppendFormat("<li>{0}</li>", s.Replace("--", string.Empty));
                }
            sb.Append("</ul>");
            return sb.ToString();
        }
        private string BuildCSSFromItems(List<Item> items)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var item in items)
            {
                sb.AppendFormat("/*---------------------{0}---------------------*/\r\n", item.Name);
                sb.AppendFormat("{0} {1} {2}\r\n", item.StyleName, '{', '}');
            }
            return sb.ToString();
        }
        private CSSDocument ParseCSS(string cssSource)
        {
            CSSParser parser = new CSSParser();
            CSSDocument document = parser.ParseText(cssSource);


            errorMessageLiteral.Text = FormatErrorMessage(parser.Errors);
            return document;
        }
        private void  DisplayCSS(CSSDocument document, ListBox listSourceCSS)
        {
            if (document== null) return ;

            listSourceCSS.Items.Clear();
            
            List<Selector> selectors = new List<Selector>();
            foreach (var rs in document.RuleSets)
                selectors.AddRange(rs.Selectors.ToArray());
            List<SimpleSelector> simpleSelectors = new List<SimpleSelector>();


            foreach (var dir in selectors)
            {
                simpleSelectors.AddRange(dir.SimpleSelectors.ToArray());
            }
            
            List<string> commonSelectors = simpleSelectors.Where(p => !p.ToString().StartsWith(".") && !p.ToString().StartsWith("#")).Select(p=>p.ToString()).ToList();
            List<string> classSelectors = simpleSelectors.Where(p => p.ToString().StartsWith(".")).Select(p => p.ToString()).ToList();
            List<string> idSelectors = simpleSelectors.Where(p => p.ToString().StartsWith("#")).Select(p => p.ToString()).ToList();
            commonSelectors = commonSelectors.OrderBy(p => p).ToList();

            commonSelectors.AddRange(idSelectors.OrderBy(p => p).ToList());
            commonSelectors.AddRange(classSelectors.OrderBy(p => p).ToList());
            foreach (var selector in commonSelectors.Distinct())
            {
                string name = selector;
                listSourceCSS.Items.Add(new ListItem() { Text=name, Value= name});
                //if(!string.IsNullOrEmpty(dir.SimpleSelectors[0].Class)) name = "."+dir.SimpleSelectors[0].Class;
                //if(!string.IsNullOrEmpty(dir.SimpleSelectors[0].ElementName)) name = dir.SimpleSelectors[0].ElementName;
                //if(!string.IsNullOrEmpty(dir.SimpleSelectors[0].ID)) name = "#"+dir.SimpleSelectors[0].ID;
                //if (listSourceCSS.Items.FindByText(name) == null)
                //{
                //    ListItem item = new ListItem(name);
                //    //RuleSet ruleset = FindRuleSet(name, document);
                //    //item.Attributes.Add("rel", ruleset != null ?ruleset.Declarations.ToString() :string.Empty);
                //    listSourceCSS.Items.Add(item);
                //}
            }
            
        }
        
        private void InitializeSharepointCSSFromDB()
        {
            List<Item> dbItems = Database.Items;
            string css = BuildCSSFromItems(dbItems);
            CSSDocument document = ParseCSS(css);
            SessionManager.ShareBrandInSession = document;
        }

        private void PopulateAreaDropDownList()
        {
            filterSharepoint.DataSource = Database.Areas;
            filterSharepoint.DataTextField = "Name";
            filterSharepoint.DataValueField = "Id";
            filterSharepoint.DataBind();
            filterSharepoint.Items.Insert(0, new ListItem("All"));
        }
        public void DisplayCSSFile(string file, ListBox listbox, out string css)
        {
            css = "";
            if (!File.Exists(file)) return;
            using (StreamReader stream = File.OpenText(file))
            {
                css = stream.ReadToEnd();
                
                DisplayCSS(ParseCSS(css), listbox);
            }
        }
        private static CSSDocument GetChangesOnly(CSSDocument sp, List<CSSMapping> cssMappings)
        {
            CSSDocument document = new CSSDocument();

            foreach (var mapping in cssMappings)
            {

                RuleSet spRule = FindRuleSet(mapping.SharepointSelector, sp);

                if (spRule == null) continue;
                //foreach (var d in spRule.Declarations)
                //{
                //    var match = sourceRule.Declarations.Where(p => p.Name == d.Name).FirstOrDefault();
                //    if (match != null)
                //    {
                //        d.Expression = match.Expression;
                //    }
                //}
                document.RuleSets.Add(spRule);
            }

            return document;

        }

        private static void MergeCSS(CSSDocument source, CSSDocument destination, List<CSSMapping> cssMappings)
        {
            foreach (var mapping in cssMappings)
            {
                RuleSet sourceRule = FindRuleSet(mapping.SourceSelector, source);

                RuleSet spRule = FindRuleSet(mapping.SharepointSelector, destination);
                if (sourceRule == null || spRule == null) continue;

                MergeRuleSet( sourceRule, spRule);
            }

        }

        private static void MergeRuleSet( RuleSet sourceRule, RuleSet spRule)
        {
            if (sourceRule == null || spRule == null) return;
            List<string> ignore = new List<string>() { "position", "z-index", "width", "height", "max-height", "max-width", "min-height", 
                                                        "min-width", "visibility", "display", "position", "top", "left", "right", "bottom" ,
                                                       "background-image","background-repeat","background-position","background-attachment"
                                                      };

            sourceRule.SplitOut();
            spRule.SplitOut();
            int count = spRule.Declarations.Count;
            for (int index = 0; index < count; index++)
            //                    foreach (var d in spRule.Declarations)
            {
                var d = spRule.Declarations[index];
                var match = sourceRule.Declarations.Where(p => !ignore.Any(s => s == p.Name) && p.Name == d.Name).FirstOrDefault();
                if (match != null)
                {
                    CopyTerm(d, match);
                }
                //else
                //{
                //    List<Declaration> others = sourceRule.Declarations.
                //                Where(p => !ignore.Any(s => s == p.Name) 
                //                    && p.Name != d.Name 
                //                    && p.Name.StartsWith(d.Name)
                //                    && !p.Name.StartsWith("border-left")
                //                    && !p.Name.StartsWith("border-right")
                //                    && !p.Name.StartsWith("border-top")
                //                    && !p.Name.StartsWith("border-bottom"))
                //                    .ToList();

                //    foreach (var genericItem in others)
                //    {
                //        if(!spRule.Declarations.Any(p=>p.Name == genericItem.Name))
                //        //spRule.Declarations.Add(genericItem);
                //        CopyTerm(d, genericItem);

                //    }
                //}
            }
            spRule.Normalize();
            spRule.Declarations.Sort();
        }

        private static void CopyTerm(Declaration d, Declaration match)
        {
            if (string.Compare(match.Name, "background", true) == 0 || match.Name.StartsWith("background"))
            {
                //d.Expression.Terms.Clear();

                foreach (var term in d.Expression.Terms)
                {
                    Term color = match.Expression.Terms.Where(p => p.Type == TermType.Hex).FirstOrDefault();

                    if (term.Type == TermType.Hex && color != null) term.Value = color.Value;

                }
                //d.Expression = Regex.Replace( d.Expression,@"url\(.*\)",string.Empty, RegexOptions.IgnoreCase);
            }
            else
                d.Expression = match.Expression;
        }

        private static RuleSet FindRuleSet(string selector, CSSDocument sp)
        {
            
            foreach (RuleSet rule in sp.RuleSets)
            {
                foreach (var item in rule.Selectors)
                {
                    if (string.Compare(item.ToString() , selector, true) ==0) return rule;
                    else
                        foreach (var sr in item.SimpleSelectors)
                            if (string.Compare(sr.ToString(), selector, true) == 0) return rule;
                }
            }
            return null;
        }

        private static List<CSSMapping> ParseMapping(string mappings)
        {
            List<CSSMapping> result = new List<CSSMapping>();

            mappings = mappings.TrimEnd(new char[] { '|' });

            string[] arr = mappings.Split(new char[] { '|' });
            foreach (string s in arr)
            {
                if (string.IsNullOrEmpty(s)) continue;

                string[] arr1 = s.Split(new char[] { ',' });
                result.Add(new CSSMapping()
                {
                    SourceSelector = arr1[0],
                    SharepointSelector = GetSPSelectorById( Convert.ToInt32(arr1[1])),
                    SessionIndex = Convert.ToInt16(arr1[2])

                });
            }
            return result;
        }

        private static string GetSPSelectorById(int id)
        {
            Item item = Database.GetItem(id);
            if (item == null) return string.Empty;
            CSSParser parser = new CSSParser();
            CSSDocument document = parser.ParseText(item.Style);

            return document.RuleSets[0].Selectors[0].ToString();
        }
        #endregion

        #region Page Methods

        [System.Web.Script.Services.ScriptMethod]
        [System.Web.Services.WebMethod(EnableSession=true)]
        
        public static List<Item> GetSPItems(string area){
            List<Item> items = new List<Item>();

            if (string.IsNullOrEmpty(area) || area=="All") {
                items =  Database.Items.OrderBy(p=>p.Name).ToList();
            }
            else
            {
                items= Database.Items.Where(p => p.AreaId.ToString() == area).OrderBy(p => p.Name).ToList();
            }

            //items.ForEach(delegate(Item item) {
            //    try
            //    {
            //        CSSParser parser = new CSSParser();
            //        CSSDocument document = parser.ParseText(item.Style);
            //        if (document != null)
            //            item.StyleName = document.RuleSets[0].Selectors[0].ToString();
            //    }
            //    catch (Exception ex) { }
            //});
            return items;

        }
        [System.Web.Script.Services.ScriptMethod]
        [System.Web.Services.WebMethod(EnableSession = true)]
        public static object GetItem(int id, string source)
        {
            string maping = source + "," + id.ToString()+ "," + (SessionManager.UploadedCSSDocuments.Count-1).ToString();
            var item = Database.GetItem(id);
            return new { Item = item, MergeSource = string.IsNullOrEmpty(source) ?item.Style:GetUpdate(maping, MergeModes.OnlyChanges) };
        }

        [System.Web.Script.Services.ScriptMethod]
        [System.Web.Services.WebMethod(EnableSession = true)]
        public static string GetUpdate(string mappings, MergeModes mode)
        {
            System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();

            CSSParser parser = new CSSParser();
            CSSDocument source = SessionManager.WorkingDocument;
            CSSDocument sp = SessionManager.StaticSharepointCSSDocument;
            CSSDocument mergedDoc = sp.Clone<CSSDocument>(); 
            
            List<CSSMapping> cssMappings = ParseMapping(mappings);
            var grouped = cssMappings.GroupBy(p => p.SessionIndex);

            foreach (var item in grouped)
            {
                if (item.Key < 0) continue;
                MergeCSS(SessionManager.UploadedCSSDocuments[item.Key], mergedDoc, cssMappings);
            }
            
           
            if (mode == MergeModes.OnlyChanges)
            {
                CSSDocument changes = GetChangesOnly(mergedDoc, cssMappings);
                return changes.ToString(false);

            }
            return mergedDoc.ToString();
        }
        [System.Web.Script.Services.ScriptMethod]
        [System.Web.Services.WebMethod(EnableSession = true)]
        public static object GetSelectorContent(string selector, int sharepointId)
        {
            CSSParser parser = new CSSParser();
            //CSSDocument document = parser.ParseFile(GetWorkingFile());
            CSSDocument document = SessionManager.WorkingDocument;

            RuleSet rule = FindRuleSet(selector, document);
            StringBuilder sb = new StringBuilder();
            if (rule != null)
            {
                foreach (var item in rule.Declarations)
                {
                    sb.AppendFormat("{0};\r\n",item.ToString());
                }
            }
            return  new {Source=sb.ToString(), MergedSource = GetUpdate(selector+","+sharepointId+ "," + (SessionManager.UploadedCSSDocuments.Count-1).ToString(), MergeModes.OnlyChanges)};

        }

        #endregion
        public enum MergeModes
        {
            All,
            OnlyChanges
        }
    }

    public class CSSMapping
    {
        public string SourceSelector { get; set; }
        public string SharepointSelector { get; set; }
        public short SessionIndex { get; set; }
    }
}